package com.sukaiyi.giteejavaapi.apifetcher;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import org.junit.jupiter.api.*;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class JsonFormatApiFetcher {

    public static final String API_DOC_URL = "https://gitee.com/api/v5/swagger";
    public static final String DIST = System.getProperty("user.dir") + "\\src\\main\\resources\\apis";

    public static final Map<String, JSONObject> persistenceMap = new HashMap<>();

    public static final Map<String, String> HEADERS = new HashMap<String, String>(){{
        put("Connection", "close");
        put("Accept", "application/json, text/plain, */*");
        put("User-Agent", "GiteeIdeaPlugin");
        put("Host", "gitee.com");
    }};

    private static WebDriver driver;

    @BeforeAll
    static void setupWebDriver() {
        System.setProperty("webdriver.chrome.driver", ".drivers\\chromedriver.exe");
        driver = new ChromeDriver();
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
    }

    @AfterAll
    static void prepareForQuit() {
        driver.quit();
    }

    @AfterAll
    static void persistence() {
        for (Map.Entry<String, JSONObject> entry : persistenceMap.entrySet()) {
            String fileName = DIST + File.separator + entry.getKey();
            File file = new File(fileName);
            Path path = file.toPath();
            try {
                if (!file.exists() && !file.createNewFile()) {
                    continue;
                }
                Files.write(path, entry.getValue().toStringPretty().getBytes(StandardCharsets.UTF_8));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    @TestFactory
    public Stream<DynamicNode> fetch() {
        driver.get(API_DOC_URL);

        List<WebElement> parts = driver.findElement(By.className("olist"))
                .findElements(By.xpath("./section/ul/li"));

        return parts.stream()
                .map(part -> {
                    String partName = part.findElement(By.xpath("./p")).getText();
                    return DynamicContainer.dynamicContainer(partName, handlePart(part));
                });
    }

    private Stream<DynamicNode> handlePart(WebElement part) {
        part.click();
        String partName = part.findElement(By.xpath("./p")).getText();
        String fileName = Optional.of(part)
                .map(p -> p.findElement(By.xpath("./p/span")))
                .map(WebElement::getText)
                .map(String::toLowerCase)
                .map(e -> e.replace(' ', '-'))
                .map(p -> p + ".json")
                .orElse(null);
        if (fileName == null) {
            return Stream.empty();
        }
        List<WebElement> items = part.findElements(By.xpath("./ul/a"));

        persistenceMap.computeIfAbsent(fileName, name -> {
            JSONObject obj = new JSONObject();
            obj.put("description", partName);
            obj.put("apis", new JSONArray());
            return obj;
        });
        return items.stream()
                .map(item -> {
                    String description = item.getText();
                    return DynamicTest.dynamicTest(description, () -> handleItem(fileName, item));
                });
    }

    private void handleItem(String fileName, WebElement item) {
        String description = item.getText();
        String itemUrl = item.getAttribute("href");
        driver.get(itemUrl);
        String name = itemUrl.substring(itemUrl.lastIndexOf('/') + 1);

        WebElement addressEle = driver.findElement(By.xpath("//div[@class=\"address\"]"));
        String method = addressEle.findElement(By.tagName("span")).getText();
        String url = addressEle.findElement(By.tagName("p")).getText();
        url = url.replace("{", "{{")
                .replace("}", "}}")
                .replace("https://gitee.com/api/v5", "{{base_url}}");

        List<WebElement> parameterEles = driver.findElement(By.xpath("//div[@class=\"parameters\"]"))
                .findElement(By.xpath("//tbody"))
                .findElements(By.tagName("tr"));
        List<Param> params = parameterEles.stream()
                .map(p -> p.findElements(By.xpath("./td")))
                .map(eles -> {
                    String paramName = eles.get(0).getText().replace("*", "");
                    String desc = eles.get(2).getText();
                    String type = eles.get(3).getText();
                    String dataType = eles.get(4).getText();
                    return new Param(paramName, desc, type, dataType);
                }).collect(Collectors.toList());

        JSONObject http = buildHttp(name, description, method, url, params);
        persistenceMap.get(fileName).getJSONArray("apis").add(http);
    }

    private JSONObject buildHttp(String name, String description, String method, String url, List<Param> params){
        JSONObject http = new JSONObject();
        http.put("name", name);
        http.put("description", description);
        http.put("method", method);
        http.put("url", url);

        JSONArray parameters = new JSONArray();
        for (Param param : params) {
            JSONObject parameter = new JSONObject();
            parameter.put("name", param.name);
            parameter.put("description", param.description);
            parameter.put("dataType", param.dataType);
            parameter.put("type", param.type);
            parameters.add(parameter);
        }
        http.put("parameters", parameters);
        http.put("headers", HEADERS);
        return http;
    }

    public static final class Param {
        private String name;
        private String description;
        private String type;
        private String dataType;

        public Param(String name, String description, String type, String dataType) {
            this.name = name;
            this.description = description;
            this.type = type;
            this.dataType = dataType;
        }
    }
}